import java.awt.Color;
import java.util.Random;
import java.util.Scanner;

import se.lth.cs.ptdc.fractal.MandelbrotGUI;

public class Generator {

	public void render(MandelbrotGUI gui) {

		gui.disableInput();
		
		Scanner scan = new Scanner(gui.getExtraText());
		int iterations = 200;
		if (scan.hasNextInt())
			iterations = scan.nextInt();

		int res = 1;
		switch (gui.getResolution()) {
		case MandelbrotGUI.RESOLUTION_VERY_LOW:
			res += 2;
		case MandelbrotGUI.RESOLUTION_LOW:
			res += 2;
		case MandelbrotGUI.RESOLUTION_MEDIUM:
			res += 2;
		case MandelbrotGUI.RESOLUTION_HIGH:
			res += 2;
		}

		int width = gui.getWidth() / res;
		int height = gui.getHeight() / res;

		Color[][] picture = new Color[height][width];
		Color[] palette = new Color[iterations + 1];
		
		if (gui.getMode() == MandelbrotGUI.MODE_BW) {
			for (int i = 0; i < palette.length; ++i) {
				palette[i] = new Color(i * 255 / iterations, i * 255 / iterations, i * 255 / iterations);
			}
		} else {
			Random rand = new Random();
			double 	r = (rand.nextDouble() * Math.PI * 14), 
					g = (rand.nextDouble() * Math.PI * 14), 
					b = (rand.nextDouble() * Math.PI * 14);
			
			for (int i = 0; i < palette.length; ++i) {
				double t = 1 - (double) i / (double) (palette.length - 1);
				int red = (int) (Math.sin(t * r - Math.PI / 2) * 127 + 127);
				int green = (int) (Math.sin(t * g - Math.PI / 2) * 127 + 127);
				int blue = (int) (Math.sin(t * b - Math.PI / 2) * 127 + 127);

				palette[i] = new Color(red, blue, green);
			}
		}
		
		boolean fast = false;
		if(scan.hasNextBoolean())
			fast = scan.nextBoolean();
		
		if(!fast){
			Complex[][] complex = this.mesh(gui.getMinimumReal(), gui.getMaximumReal(), 
					gui.getMinimumImag(), gui.getMaximumImag(),
					width, height);

			for (int i = 0; i < height; ++i) {
				for (int j = 0; j < width; ++j) {

					Complex temp = new Complex(	complex[i][j].getRe(), 
							complex[i][j].getIm());
					int k = 0;
					while (k < iterations && complex[i][j].getAbs2() <= 4) {
						complex[i][j].mul(complex[i][j]);
						complex[i][j].add(temp);
						++k;
					}

					picture[i][j] = palette[k];
				}
			}
		}
		else{
			double minRe = gui.getMinimumReal();
			double maxRe = gui.getMaximumReal();
			double minIm = gui.getMinimumImag();
			double maxIm = gui.getMaximumImag();
			for (int i = 0; i < height; ++i) {
				for (int j = 0; j < width; ++j) {

					double re0 = j * (maxRe - minRe) / (width - 1) + minRe;
					double im0 = (height - 1 - i) * (maxIm - minIm) / (height - 1) + minIm;

					double re1 = re0;
					double im1 = im0;

					double re2 = re1 * re1;
					double im2 = im1 * im1;

					int k = 0;
					while(re2 + im2 < 4 && k < iterations){
						im1 = 2 * re1 * im1 + im0;
						re1 = re2 - im2 + re0;
						re2 = re1 * re1;
						im2 = im1 * im1;
						++k;
					}

					picture[i][j] = palette[k];
				}
			}
		}
		
		gui.putData(picture, res, res);
		gui.enableInput();
	}

	private Complex[][] mesh(double minRe, double maxRe, double minIm,
			double maxIm, int width, int height) {
		Complex[][] result = new Complex[height][width];

		for (int i = 0; i < height; ++i) {
			for (int j = 0; j < width; ++j) {
				result[i][j] = new Complex(j * (maxRe - minRe) / (width - 1) + minRe, 
						(height - 1 - i) * (maxIm - minIm) / (height - 1) + minIm);
			}
		}

		return result;
	}
}
